/**
 * 
 */
package org.quickbundle.project.filter;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import com.dfhc.util.StringHelper;

/**
 * @author 龙色波
 * 脚本攻击包装器 xss
 */
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {



	private static final Pattern REMOVE_INPUT_TAG = Pattern.compile("<[\r\n| | ]*input(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
	private static final Pattern REMOVE_START_TEXT_AREA_TAG = Pattern.compile("<[\r\n| | ]*textarea(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
	private static final Pattern REMOVE_END_TEXT_AREA_TAG = Pattern.compile("</[\r\n| | ]*textarea[\r\n| | ]*>", Pattern.CASE_INSENSITIVE);
	private static final Pattern REMOVE_VBSCRIPT = Pattern.compile("vbscript[\r\n| | ]*:[\r\n| | ]*", Pattern.CASE_INSENSITIVE);
	private static final Pattern REMOVE_SCRIPT = Pattern.compile("javascript[\r\n| | ]*:[\r\n| | ]*", Pattern.CASE_INSENSITIVE);
	private static final Pattern REMOVE_E_XPRESSION = Pattern.compile("e-xpression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
	private static final Pattern REMOVE_ALERT_TAG = Pattern.compile("alert\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
	private static final Pattern REMOVE_EVAL_TAG = Pattern.compile("eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
	private static final Pattern REMOVE_START_SCRIPT_TAG = Pattern.compile("<[\r\n| | ]*script(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
	private static final Pattern REMOVE_END_SCRIPT_TAG = Pattern.compile("</[\r\n| | ]*script[\r\n| | ]*>", Pattern.CASE_INSENSITIVE);
	private static final Pattern SCRIPT_PATTERN = Pattern.compile("src[\r\n| | ]*=[\r\n| | ]*[\\\"|\\\'](.*?)[\\\"|\\\']", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
	private static final Pattern COMPILE = Pattern.compile("<[\r\n| | ]*script[\r\n| | ]*>(.*?)</[\r\n| | ]*script[\r\n| | ]*>", Pattern.CASE_INSENSITIVE);
	HttpServletRequest orgRequest = null;
	private static List<String> eventList;
	static{
	eventList = new ArrayList<String>();
	eventList.add("onabort");
	eventList.add("onblur");
	eventList.add("onchange");
	eventList.add("onclick");    	
	eventList.add("ondblclick");
	eventList.add("onerror");
	eventList.add("onfocus");
	eventList.add("onkeydown");
	eventList.add("onkeypress");
	eventList.add("onkeyup");
	eventList.add("onload");
	eventList.add("onmousedown");
	eventList.add("onmousemove");
	eventList.add("onmouseout");
	eventList.add("onmouseover");
	eventList.add("onmouseup");    	
	
	eventList.add("onreset");
	eventList.add("onresize");
	eventList.add("onselect");
	eventList.add("onsubmit");
	eventList.add("onunload");
	}
    public XssHttpServletRequestWrapper(HttpServletRequest request) {  
        super(request);  
        orgRequest = request;  
    }  
    

	/** 
     * 覆盖getParameter方法，将参数名和参数值都做xss 过滤。<br/> 
     * 如果需要获得原始的值，则通过super.getParameterValues(name)来获取<br/> 
     * getParameterNames,getParameterValues和getParameterMap也可能需要覆盖 
     */  
    @Override  
    public String getParameter(String name) {  
    	//System.out.println("parameter name:"+name);
        String value = super.getParameter(xssEncode(name));  
        if (value != null) {  
            value = xssEncode(value);  
        }  
        return value;  
    }  
  
    @Override
   	public String[] getParameterValues(String name) {
   		// TODO Auto-generated method stub
   		String[] value=super.getParameterValues(name);
   		if(value==null || value.length==0){
   			return value;
   		}
   		for(int i=0;i<value.length;i++){
   			value[i]=xssEncode(value[i]);
   		}
   		return value;
   	}
    @Override
	public Object getAttribute(String name) {
		// TODO Auto-generated method stub
    	Object value=(Object) super.getAttribute(name);
		if(value instanceof String){
			if(value==null || ((String)value).trim().length()==0){
				return value;
			}else{
				return xssEncode((String)value);
			}
		}
		return value;
	}
    @Override  
    public Map getParameterMap() {  
        super.getContextPath();  
        Map<String,String[]> map = super.getParameterMap();  
        if(!map.isEmpty()){  
            Set<String> keySet = map.keySet();  
            Iterator<String> keyIt = keySet.iterator();  
            while(keyIt.hasNext()){  
                String key = keyIt.next();  
//              String value = map.get(key)[0];  
//              map.get(key)[0] = this.replaceParam(value);  
                //这边实现对整个数组的判断。  
                String[] values=map.get(key);  
                for(int i=0;i<values.length;i++){  
                    map.get(key)[i]=xssEncode(values[i]);  
                }  
            }  
        }  
        return map;  
    }  

    /** 
     * 覆盖getHeader方法，将参数名和参数值都做xss 过滤。<br/> 
     * 如果需要获得原始的值，则通过super.getHeaders(name)来获取<br/> 
     * getHeaderNames 也可能需要覆盖 
     */  
    @Override  
    public String getHeader(String name) {  
  
        String value = super.getHeader(xssEncode(name));  
        if (value != null) {  
            value = xssEncode(value);  
        }  
        return value;  
    }  
  
    /** 
     * 将容易引起xss & sql漏洞的半角字符直接替换成全角字符 
     *  
     * @param s 
     * @return 
     */  
    private static String xssEncode(String s) {  
        if (s == null || s.isEmpty()) {  
            return s;  
        }else{  
            s = stripXSSAndSql(s);
            return s;
        }  
//        StringBuilder sb = new StringBuilder(s.length() + 16);  
//        for (int i = 0; i < s.length(); i++) {  
//            char c = s.charAt(i);  
//            switch (c) {  
//            case '>':  
//                sb.append("＞");// 转义大于号  
//                break;  
//            case '<':  
//                sb.append("＜");// 转义小于号  
//                break;  
//            case '\'':  
//                sb.append("＇");// 转义单引号  
//                break;  
//            case '\"':  
//                sb.append("＂");// 转义双引号  
//                break;  
//            case '&':  
//                sb.append("＆");// 转义&  
//                break;  
//            case '#':  
//                sb.append("＃");// 转义#  
//                break;  
//            default:  
//                sb.append(c);  
//                break;  
//            }  
//        }  
//        return sb.toString();  
    }  
  
    /** 
     * 获取最原始的request 
     *  
     * @return 
     */  
    public HttpServletRequest getOrgRequest() {  
        return orgRequest;  
    }  
  
    /** 
     * 获取最原始的request的静态方法 
     *  
     * @return 
     */  
    public static HttpServletRequest getOrgRequest(HttpServletRequest req) {  
        if (req instanceof XssHttpServletRequestWrapper) {  
            return ((XssHttpServletRequestWrapper) req).getOrgRequest();  
        }  
  
        return req;  
    }  
  
    /** 
     *  
     * 防止xss跨脚本攻击（替换，根据实际情况调整） 
     */  
  
    public static String stripXSSAndSql(String value) {  
        if (value != null) {  
            // NOTE: It's highly recommended to use the ESAPI library and  
            // uncomment the following line to  
            // avoid encoded attacks.  
            // value = ESAPI.encoder().canonicalize(value);  
            // Avoid null characters  
/**         value = value.replaceAll("", "");***/  
            // Avoid anything between script tags  
//        	System.out.println("value="+value);
        	//debug
        	if(value.indexOf("alert")!=-1){
        		System.out.println(value);
        	}
        	//替换转义符号
        	value = StringHelper.replaceEscape(value);
            Pattern scriptPattern = COMPILE;  
            value = scriptPattern.matcher(value).replaceAll("");  
            // Avoid anything in a src="http://www.yihaomen.com/article/java/..." type of e-xpression  
            scriptPattern = SCRIPT_PATTERN;  
            value = scriptPattern.matcher(value).replaceAll("");  
            // Remove any lonesome </script> tag  
            scriptPattern = REMOVE_END_SCRIPT_TAG;  
            value = scriptPattern.matcher(value).replaceAll("");  
            // Remove any lonesome <script ...> tag  
            scriptPattern = REMOVE_START_SCRIPT_TAG;  
            value = scriptPattern.matcher(value).replaceAll("");  
            // Avoid eval(...) expressions  
            scriptPattern = REMOVE_EVAL_TAG;  
            value = scriptPattern.matcher(value).replaceAll("");  
            // Avoid alert(...) expressions  
            scriptPattern = REMOVE_ALERT_TAG;  
            value = scriptPattern.matcher(value).replaceAll("");  
            // Avoid e-xpression(...) expressions  
            scriptPattern = REMOVE_E_XPRESSION;  
            value = scriptPattern.matcher(value).replaceAll("");  
            // Avoid javascript:... expressions  
            scriptPattern = REMOVE_SCRIPT;  
            value = scriptPattern.matcher(value).replaceAll("");  
            // Avoid vbscript:... expressions  
            scriptPattern = REMOVE_VBSCRIPT;  
            value = scriptPattern.matcher(value).replaceAll("");  
            // Avoid onload= expressions  
//            scriptPattern = Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);  
//            value = scriptPattern.matcher(value).replaceAll("");  
            // Remove any lonesome </textarea> tag  
            scriptPattern = REMOVE_END_TEXT_AREA_TAG;  
            value = scriptPattern.matcher(value).replaceAll("");  
            // Remove any lonesome <textarea ...> tag  
            scriptPattern = REMOVE_START_TEXT_AREA_TAG;  
            value = scriptPattern.matcher(value).replaceAll("");  
            // Remove any lonesome <input ...> tag  
            scriptPattern = REMOVE_INPUT_TAG;  
            value = scriptPattern.matcher(value).replaceAll("");
            //替换事件列表
            for(String eventName:eventList){
               scriptPattern = Pattern.compile(eventName+"(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);  
               value = scriptPattern.matcher(value).replaceAll("");  
            }
            //替换掉<,>,iframe,img,anchor,data:
            value = StringHelper.replaceAll(value, "<", "").toString();
            value = StringHelper.replaceAll(value, ">", "").toString();
            value = StringHelper.replaceAll(value, "iframe", "").toString();
            value = StringHelper.replaceAll(value, "img", "").toString();
            value = StringHelper.replaceAll(value, "anchor", "").toString();
            value = StringHelper.replaceAll(value, "data:", "").toString();
            
        }  
        return value;  
    }  
}
